package service

import (
	"errors"
	"fmt"
	"gitee.com/spellbreaker/FogGoService/fogcore"
	socketio "github.com/googollee/go-socket.io"
	"log"
	"math/rand"
	"strconv"
	"sync"
	"time"
)

const n = 9                    //默认棋盘大小
const DF_RT = time.Minute * 10 //默认给玩家每人10分钟

//保存全部房间
var rooms = Rooms{playerMap: make(map[string]*GameRoom)}

//uid和rid的map，用户id和房间id
var uid2rid sync.Map

//Connect 连接socket.io时触发函数，校验用户信息
func Connect(s socketio.Conn) error {
	defer dfRecover()
	url := s.URL()
	query := url.Query()
	username, ok1 := query["username"]
	password, ok2 := query["password"]
	if !ok1 || !ok2 {
		s.Close()
		return errors.New("用户名或者密码为空")
	}
	uid := login(username[0], password[0])
	if uid == "" {
		s.Close()
		return errors.New("用户名或者密码错误")
	}
	s.SetContext(uid)
	log.Println("connected:", s.ID(), username, password)
	return nil
}

func inRoom(uid string) bool {
	_, ok := uid2rid.Load(uid)
	return ok
}

//CreateRoom 创建房间
func CreateRoom(s socketio.Conn, rid string) string {
	defer dfRecover()
	log.Println("创建房间，房间id是", rid)
	//先判断自身是否在房间内
	uid := s.Context().(string)
	if inRoom(uid) {
		return "已经在房间里了"
	}
	//判断房间是否存在，并创建房间
	ok := rooms.AppendGameRoom(rid, &GameRoom{
		gsmap: map[string]*GameSelf{
			s.Context().(string): {
				username: getUsername(s),
				color:    fogcore.C_BLACK,
				conn:     s,
				rt:       DF_RT,
			},
		},
	})
	if !ok {
		return "房间已存在"
	}
	//关联uid和rid
	uid2rid.Store(s.Context().(string), rid)
	log.Printf("用户id:%s,创建房间，房间id:%s", s.Context().(string), rid)
	return fmt.Sprintf("创建房间，房间id:%s", rid)
}

//JoinRoom 加入房间
func JoinRoom(s socketio.Conn, rid string) string {
	room := rooms.GetGameRoomByRID(rid)
	if room == nil {
		return "房间不存在"
	}
	uid := s.Context().(string)
	if inRoom(uid) {
		return "已经在房间里了"
	}
	room.gLock.Lock()
	defer room.gLock.Unlock()
	room.fogGo = fogcore.NewFogGoGame(n, fogcore.C_BLACK)
	room.gsmap[uid] = &GameSelf{
		username: getUsername(s),
		color:    fogcore.C_WHITE,
		mask:     room.fogGo.FogGoMask(fogcore.C_WHITE),
		conn:     s,
		rt:       DF_RT,
	}
	room.gameState = GAME_STARTED
	room.getOpponent(uid).mask = room.fogGo.FogGoMask(fogcore.C_BLACK)
	//关联uid和rid
	uid2rid.Store(uid, rid)
	//向房主发送通知
	room.getOpponent(uid).conn.Emit("joined", getUsername(s)+"加入了房间")

	room.getOpponent(uid).conn.Emit("gameStart", fogcore.View(room.fogGo, fogcore.C_BLACK))
	s.Emit("gameStart", fogcore.View(room.fogGo, fogcore.C_WHITE))

	return "加入房间成功，房主为:" + room.getOpponent(uid).username
}

//TryPlay 在棋盘上悬浮时提示
func TryPlay(s socketio.Conn, vec fogcore.Vec2) ResultMsg {
	if fogcore.RangeCheck(n, vec) {
		return ResultError("越界了")
	}
	uid := s.Context().(string)
	room := getRoom(uid)
	if room == nil {
		return ResultError("请先加入房间")
	}
	room.gLock.RLock()
	defer room.gLock.RUnlock()
	code, err := room.gsmap[uid].mask.CanPlaced(vec)
	if code == fogcore.C_PLAYABLE {
		return ResultSuccess(nil)
	}
	return ResultError(err.Error())
}

//Play 落子
func Play(s socketio.Conn, vec fogcore.Vec2) string {
	log.Println(vec)
	if fogcore.RangeCheck(n, vec) {
		return "越界了"
	}
	uid := s.Context().(string)
	room := getRoom(uid)
	if room == nil {
		return "请先加入房间"
	}
	room.gLock.RLock()
	if room.fogGo.PieceColor != room.gsmap[uid].color {
		room.gLock.RUnlock()
		return "未到你落子"
	}
	room.gLock.RUnlock()
	room.gLock.Lock()
	defer room.gLock.Unlock()
	//TODO:还未进行超时判断
	//room.gsmap[uid].rt
	//落子
	code, err := room.fogGo.Placed(vec)
	if err != nil {
		if code != fogcore.C_CF_LZ {
			room.gsmap[uid].mask = room.fogGo.FogGoMask(room.gsmap[uid].color)
		}
		return err.Error()
	}
	//修改对应的视图，视图就是下棋着能看到的棋盘
	room.gsmap[uid].mask = room.fogGo.FogGoMask(room.gsmap[uid].color)
	room.getOpponent(uid).mask = room.fogGo.FogGoMask(room.getOpponent(uid).color)

	//通知对方落子
	room.getOpponent(uid).conn.Emit("opPlayed", vec)
	return room.gsmap[uid].mask.Board.Print(room.gsmap[uid].color)
}

//Disconnect 玩家断开连接
func Disconnect(s socketio.Conn, reason string) {
	defer dfRecover()
	log.Printf("%s退出连接,%s", getUsername(s), reason)
	//断开连接时，要删除uid2rid和rooms中的信息，防止内存泄露
	if s.Context() == nil {
		return
	}

	uid := s.Context().(string)
	rid, ok := uid2rid.Load(uid)
	if ok { //玩家是否开启对局
		room := rooms.GetGameRoomByRID(rid.(string))
		room.gLock.Lock()
		defer room.gLock.Unlock()
		if room.gameState == NOT_START || room.gameState == GAME_OVER {
			uid2rid.Delete(uid)
			rooms.DeleteGameRoomByRID(rid.(string))
			return
		}
		room.gsmap[uid].disconnect = true
		op := room.getOpponent(uid)
		if op.disconnect { //如果双方都断开连接
			uid2rid.Delete(uid)
			rooms.DeleteGameRoomByRID(rid.(string))
			uid2rid.Delete(op.uid)
		}
	}

}

func dfRecover() {
	if err := recover(); err != nil {
		log.Println("默认错误：", err)
	}
}

//模拟登录，TODO:数据库连接未实现
func login(username, password string) string {
	return strconv.Itoa(rand.Intn(1000))
}

//从socket.io连接中获取用户名
func getUsername(s socketio.Conn) string {
	url := s.URL()
	query := url.Query()
	return query["username"][0]
}

//提供uid获取加入的房间
func getRoom(uid string) *GameRoom {
	rid, ok := uid2rid.Load(uid)
	if !ok {
		return nil
	}
	return rooms.GetGameRoomByRID(rid.(string))
}
